package zy.blue7.flowable.service;

import org.flowable.engine.*;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.DeploymentBuilder;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:flowable-context.xml")
public class RuntimeServiceTest {

    /**
     * todo  RuntimeService类说明 运行服务类
     *  该类核心的功能就是启动实例，查询与实例相关的一些信息
     *  类似于我们Java中的实例化一个对象，然后操作这个实例对象
     *
     *  2.是flowable的流程执行服务类。可以从这个服务类中获取很多相关于 流程执行相关的信息
     *  实现类 为  RuntimeServiceImpl
     *  提供了一系列根据自身业务场景 启动流程实例的api
     *
     *  todo 可以派生的类
     *      可以产生ProcessInstanceBuilder， 用来定义启动流程实例的相关参数
     *
     */

    /**
     * todo 实例运转系列
     * 支持启动实例的方式
     * 1.processDefinitionId
     * 2.processDefinitionKey
     * 3.processDefinitionKey + tenantId   不同不同系统统一流程
     * 4.messageName
     * 5.messageName+ tenantId
     * <p>
     * <p>
     * todo 流程定义，执行实例，流程实例概念：
     * 流程定义：
     * 流程定义类》我们可以从这里获取资源文件，等等，包含bpmn文件等等
     * 类似于我能定义的Java 类，提前定义一些规则
     * <p>
     * todo 流程实例：
     * 1.代表流程定义的实例。 如 学生张三请了三天假，他就必须 发出一个流程实例的申请
     * 2.一个流程实例 包括了所有的运行节点，我们可以利用这个对象来了解当前流程实例的进度等信息
     * 3.流程实例 就代表一个流程从开始到结束的最大的流程分支， 即一个流程中流程实例只有一个。
     * 4.流程实例 通常也叫做 执行实例的根节点
     * <p>
     * <p>
     * todo 执行实例
     * 1.public interface ProcessInstance extends Execution
     * 2.从源代码中可以看出 ProcessInstance 就是Execution、
     * 3.启动流程的时候会首先创建流程实例，然后创建执行实例
     * 4.流程运转过程中永远执行的是自己对应的的执行实例
     * 5.当所有的执行实例按照规则执行完毕后，则实例随之结束
     * <p>
     * todo
     * 6.flowable 用这个对象去描述流程执行过程中的每一个节点
     * 7.流程按照流程定义的规则执行一次的过程，就可以代表执行对象Execution
     * 8.一个流程，执行对象可以存在 多个， 但是流程实例只能有一个
     * 9.执行实例的父级或者父级的父级为流程实例
     * 10.执行实例可能会暂停到 某一个活动节点，需要我们去执行特定的 api去完成，然后执行实例继续往下走
     */


    ProcessEngine processEngine;
    RuntimeService runtimeService;
    RepositoryService repositoryService;
    TaskService taskService;

    @Before
    public void testProcessEngine() {

        /**
         * 获取默认的流程引擎类
         */
        //todo 主要是通过flowable的风格的配置文件  创建一个 {@see org.flowable.engine.impl.cfg.StandaloneProcessEngineConfiguration} 配置类
        //todo 该配置类 中创建了 多个service， 然后赋值给processengine
        processEngine = ProcessEngines.getDefaultProcessEngine();

        System.out.println("默认的流程引擎类 " + processEngine);

        //todo 获取的service

        DynamicBpmnService dynamicBpmnService = processEngine.getDynamicBpmnService();
        System.out.println(dynamicBpmnService);

        FormService formService = processEngine.getFormService();
        System.out.println(formService);

        HistoryService historyService = processEngine.getHistoryService();
        System.out.println(historyService);

        IdentityService identityService = processEngine.getIdentityService();
        System.out.println(identityService);

        ManagementService managementService = processEngine.getManagementService();
        System.out.println(managementService);

        repositoryService = processEngine.getRepositoryService();
        System.out.println(repositoryService);

        runtimeService = processEngine.getRuntimeService();
        System.out.println(runtimeService);

        taskService = processEngine.getTaskService();
        System.out.println(taskService);

        ProcessEngineConfiguration processEngineConfiguration = processEngine.getProcessEngineConfiguration();
        System.out.println(processEngineConfiguration);

        String name = processEngine.getName();
        System.out.println("流程引擎的名字：" + name);

    }


    @Test
    public void deploy() {
        DeploymentBuilder deploymentBuilder = repositoryService.createDeployment()
                .name("请假流程")
                .category("leave")
                .addClasspathResource("leave.bpmn20.xml");
        Deployment deployment = deploymentBuilder.deploy();
        System.out.println(deployment.getId());

    }


    /**
     * 通过 processDefinitionKey启动流程
     */
    @Test
    public void startProcessInstanceByKey() {
        /**
         * todo 开启一个流程定义实例
         * 会创建一个流程实例，
         * 也会创建多个执行实例，每个执行实例 执行到某一个节点时，会暂停
         */
        String processDefinitionKey = "leave";
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey);
        System.out.println(processInstance.getName());
        System.out.println(processInstance.getId());
        System.out.println(processInstance.getClass());
        System.out.println(processInstance.getParentId());
    }


    /**
     * 根据流程定义的key 启动流程实例说明
     * todo
     *      这里使用runtimeService启动流程实例
     *      操作数据库的 act_ru_execution表， 如果是用户任务节点，同时 也会在act_ru_task 添加一条记录
     *      执行实例走到任务节点的时候，会暂时处于等待状态，我们需要完成这个节点，执行实例才能继续开始运转
     *
     *
     */


    /**
     * 查询个人任务
     * todo 说明
     * 1.因为是任务查询，所以从processEngine冲应该得到 taskService
     * 2.使用taskService获取到任务查询对象taskQuery
     * 3.为查询对象添加查询过滤条件，使用taaskService指定任务的办理者（即查询指定用户的待办任务），同时可以添加分页排序等等过滤条件
     * 4.调用list方法执行查询，返回办理者为指定用户的任务列表
     */
    @Test
    public void createTaskQuery() {
        String assignee = "张三";
        List<Task> list = taskService.createTaskQuery()
//                .taskCandidateOrAssigned(assignee)
                //todo 等等一系列查询条件
                .taskAssignee(assignee)
                .list();
        for (Task task : list) {
            System.out.println(task.getAssignee());//任务分配人
            System.out.println(task.getOwner());
            System.out.println(task.getId());
            System.out.println(task.getDelegationState());
            System.out.println("=============");
            System.out.println(task.getExecutionId());// todo 执行实例id
            System.out.println(task.getParentTaskId());
            System.out.println(task.getProcessDefinitionId());//todo 流程定义id  ---》表act_re_procdef 的id
            System.out.println(task.getProcessInstanceId());//todo 流程实例id
        }

    }

    @Test
    public void query() {


        /**
         * select distinct RES.* from ACT_RU_TASK RES WHERE RES.ASSIGNEE_ = ? order by RES.ID_ asc
         */
//        String assignee = "张三";
//        List<Task> list = taskService.createTaskQuery()
////                .taskCandidateOrAssigned(assignee)
//                //todo 等等一系列查询条件
//                .taskAssignee(assignee)
//                .list();


        /**
         * select distinct RES.* from ACT_RU_TASK RES WHERE RES.ASSIGNEE_ = ? and ( ( RES.ASSIGNEE_ is null and exists(select LINK.ID_ from ACT_RU_IDENTITYLINK LINK where LINK.TYPE_ = 'candidate' and LINK.TASK_ID_ = RES.ID_ and ( LINK.GROUP_ID_ IN ( ? , ? ) ) ) ) ) order by RES.ID_ asc
         */
//        String assignee = "张三";
//        List<Task> list = taskService.createTaskQuery()
////                .taskCandidateOrAssigned(assignee)
//                //todo 等等一系列查询条件
//                .taskAssignee(assignee)
//                .or()//todo 错误使用方法， 使用or函数时，还需要使用 endor函数结束 ，然后在将这个整体 添加， 再加个and
//                .taskCandidateGroupIn(Arrays.asList("234", "123"))
//                .list();


        /**
         * select distinct RES.* from ACT_RU_TASK RES WHERE RES.ASSIGNEE_ = ? and RES.ASSIGNEE_ is null and exists(select LINK.ID_ from ACT_RU_IDENTITYLINK LINK where LINK.TYPE_ = 'candidate' and LINK.TASK_ID_ = RES.ID_ and ( LINK.GROUP_ID_ IN ( ? , ? ) ) ) order by RES.ID_ asc
         */
//        String assignee = "张三";
//        List<Task> list = taskService.createTaskQuery()
////                .taskCandidateOrAssigned(assignee)
//                //todo 等等一系列查询条件
//                .taskAssignee(assignee)
//                .taskCandidateGroupIn(Arrays.asList("123333", "qew"))
//                .list();


        /**
         * select distinct RES.* from ACT_RU_TASK RES WHERE ( RES.ASSIGNEE_ = ? or ( RES.ASSIGNEE_ is null and exists(select LINK.ID_ from ACT_RU_IDENTITYLINK LINK where LINK.TYPE_ = 'candidate' and LINK.TASK_ID_ = RES.ID_ and ( LINK.GROUP_ID_ IN ( ? , ? ) ) ) ) ) order by RES.ID_ asc
         */
        /**
         * todo or 函数的正确用法
         * todo 使用or函数时，还需要使用endor函数结尾， 然后会把这两个函数之间的查询条件当作一个整体，里面的使用or 条件相加
         */
//        String assignee = "张三";
//        List<Task> list = taskService.createTaskQuery()
////                .taskCandidateOrAssigned(assignee)
//                .or()
//                //todo 等等一系列查询条件
//                .taskAssignee(assignee)
//                .taskCandidateGroupIn(Arrays.asList("123333", "qew"))
//                .endOr()
//                .list();


        /**
         * select distinct RES.* from ACT_RU_TASK RES WHERE ( RES.ASSIGNEE_ = ? or ( RES.ASSIGNEE_ is null and exists(select LINK.ID_ from ACT_RU_IDENTITYLINK LINK where LINK.TYPE_ = 'candidate' and LINK.TASK_ID_ = RES.ID_ and ( LINK.USER_ID_ = ? or LINK.GROUP_ID_ IN ( ? , ? ) ) ) ) ) order by RES.ID_ asc
         */
        /**
         * todo 这里是根据 用户代办人，候选人，和候选组进行查询，三种条件都满足
         */
//        String assignee = "张三";
//        List<Task> list = taskService.createTaskQuery()
////                .taskCandidateOrAssigned(assignee)
//                .or()
//                //todo 等等一系列查询条件
//                .taskAssignee(assignee)
//                .taskCandidateGroupIn(Arrays.asList("123333", "qew"))
//                .taskCandidateUser("asdsdad")
//                .endOr()
//                .list();


        /**
         * select distinct RES.* from ACT_RU_TASK RES WHERE RES.PROC_INST_ID_ = ? and ( RES.ASSIGNEE_ = ? or ( RES.ASSIGNEE_ is null and exists(select LINK.ID_ from ACT_RU_IDENTITYLINK LINK where LINK.TYPE_ = 'candidate' and LINK.TASK_ID_ = RES.ID_ and ( LINK.USER_ID_ = ? or LINK.GROUP_ID_ IN ( ? , ? ) ) ) ) ) order by RES.ID_ asc
         */
//        String assignee = "张三";
//        List<Task> list = taskService.createTaskQuery()
//                .processInstanceId("asdasdasdassdsad--------")
//                .or()
//                //todo 等等一系列查询条件
//                .taskAssignee(assignee)
//                .taskCandidateGroupIn(Arrays.asList("123333", "qew"))
//                .taskCandidateUser("asdsdad")
//                .endOr()
//                .list();


        /**
         * select distinct RES.* from ACT_RU_TASK RES WHERE RES.PROC_INST_ID_ = ? and ( ( RES.ASSIGNEE_ is null and exists(select LINK.ID_ from ACT_RU_IDENTITYLINK LINK where LINK.TYPE_ = 'candidate' and LINK.TASK_ID_ = RES.ID_ and ( LINK.USER_ID_ = ? ) ) ) ) order by RES.ID_ asc
         */
        // TODO: 2021/9/16  or 函数和  endor 函数之间必须有查询条件
        String assignee = "张三";
        List<Task> list = taskService.createTaskQuery()
                .processInstanceId("asdasdasdassdsad--------")
                .or()
                .taskCandidateUser("asdsdad")
                .endOr()
                .list();

        System.out.println("------------------");
    }


    /**
     * todo 完成任务
     */
    @Test
    public void completeMyTask() {
        String taskId = "15002";
        taskService.complete(taskId);
    }

    /**
     * 查询当前流程实例
     */
    @Test
    public void createProcessInstanceQuery() {
        String processInstanceId = "20001";
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceId(processInstanceId)
                .singleResult();

        if (processInstance == null) {
            System.out.println("当前流程实例已经结束");
        } else {
            System.out.println(processInstance.getParentId());
            System.out.println(processInstance.getId());
            System.out.println(processInstance.getClass());
            System.out.println(processInstance.getProcessInstanceId());
            System.out.println(processInstance.getRootProcessInstanceId());
            System.out.println("当前流程实例正在运行");
        }
    }
}
